<!DOCTYPE html>





<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 3.9.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/favicon.ico?v=7.4.0">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.ico?v=7.4.0">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.ico?v=7.4.0">
  <link rel="mask-icon" href="/images/logo.svg?v=7.4.0" color="#222">

<link rel="stylesheet" href="/css/main.css?v=7.4.0">


<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css?v=4.7.0">


<script id="hexo-configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '7.4.0',
    exturl: false,
    sidebar: {"position":"left","display":"post","offset":12,"onmobile":false,"dimmer":false},
    copycode: {"enable":false,"show_result":false,"style":null},
    back2top: {"enable":true,"sidebar":false,"scrollpercent":false},
    bookmark: {"enable":false,"color":"#222","save":"auto"},
    fancybox: false,
    mediumzoom: false,
    lazyload: false,
    pangu: false,
    algolia: {
      appID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    },
    localsearch: {"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
    path: 'search.json',
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    translation: {
      copy_button: '复制',
      copy_success: '复制成功',
      copy_failure: '复制失败'
    },
    sidebarPadding: 40
  };
</script>

  <meta name="description" content="Protocol Buffer是Google的语言中立的，平台中立的，可扩展机制的，用于序列化结构化数据 - 对比XML，但更小，更快，更简单。您可以定义数据的结构化，然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。 1. 定义消息类型先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式，每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数，以及">
<meta name="keywords" content="golang,protobuf">
<meta property="og:type" content="article">
<meta property="og:title" content="protobuf3语法指南">
<meta property="og:url" content="https://unix2dos.github.io/p/21122343.html">
<meta property="og:site_name" content="Levon&#39;s Blog">
<meta property="og:description" content="Protocol Buffer是Google的语言中立的，平台中立的，可扩展机制的，用于序列化结构化数据 - 对比XML，但更小，更快，更简单。您可以定义数据的结构化，然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。 1. 定义消息类型先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式，每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数，以及">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2019-09-27T15:36:51.215Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="protobuf3语法指南">
<meta name="twitter:description" content="Protocol Buffer是Google的语言中立的，平台中立的，可扩展机制的，用于序列化结构化数据 - 对比XML，但更小，更快，更简单。您可以定义数据的结构化，然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。 1. 定义消息类型先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式，每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数，以及">
  <link rel="canonical" href="https://unix2dos.github.io/p/21122343">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome: false,
    isPost: true,
    isPage: false,
    isArchive: false
  };
</script>

  <title>protobuf3语法指南 | Levon's Blog</title>
  








  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .logo,
  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">
  <div class="container use-motion">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-meta">

    <div>
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Levon's Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <h1 class="site-subtitle" itemprop="description">微信:  L6241425</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
      
      
      
        
        <li class="menu-item menu-item-home">
      
    

    <a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-archives">
      
    

    <a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i> <br>时光</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-categories">
      
    

    <a href="/categories/" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i> <br>分类</a>

  </li>
      
      
      
        
        <li class="menu-item menu-item-tags">
      
    

    <a href="/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i> <br>标签</a>

  </li>
      <li class="menu-item menu-item-search">
        <a href="javascript:;" class="popup-trigger">
        
          <i class="menu-item-icon fa fa-search fa-fw"></i> <br>搜索</a>
      </li>
    
  </ul>

</nav>
  <div class="site-search">
    <div class="popup search-popup">
    <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocorrect="off" autocapitalize="none"
           placeholder="搜索..." spellcheck="false"
           type="text" id="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result"></div>

</div>
<div class="search-pop-overlay"></div>

  </div>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
            

          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
      <article itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block post">
    <link itemprop="mainEntityOfPage" href="https://unix2dos.github.io/p/21122343.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Levon">
      <meta itemprop="description" content="Never Give Up">
      <meta itemprop="image" content="/images/avatar.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Levon's Blog">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">protobuf3语法指南

          
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              
                
              

              <time title="创建时间：2019-08-02 19:54:01" itemprop="dateCreated datePublished" datetime="2019-08-02T19:54:01+08:00">2019-08-02</time>
            </span>
          
            

            
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2019-09-27 23:36:51" itemprop="dateModified" datetime="2019-09-27T23:36:51+08:00">2019-09-27</time>
              </span>
            
          
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/3-计算机系统/" itemprop="url" rel="index"><span itemprop="name">3-计算机系统</span></a></span>

                
                
                  ，
                
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/3-计算机系统/计算机基础/" itemprop="url" rel="index"><span itemprop="name">计算机基础</span></a></span>

                
                
              
            </span>
          

          
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="fa fa-comment-o"></i>
      </span>
        
      
      <span class="post-meta-item-text">Disqus：</span>
    
    <a title="disqus" href="/p/21122343.html#comments" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="p/21122343.html" itemprop="commentCount"></span></a>
  </span>
  
  
          <br>
            <span class="post-meta-item" title="本文字数">
              <span class="post-meta-item-icon">
                <i class="fa fa-file-word-o"></i>
              </span>
              
                <span class="post-meta-item-text">本文字数：</span>
              
              <span>20k</span>
            </span>
          
            <span class="post-meta-item" title="阅读时长">
              <span class="post-meta-item-icon">
                <i class="fa fa-clock-o"></i>
              </span>
              
                <span class="post-meta-item-text">阅读时长 &asymp;</span>
              
              <span>18 分钟</span>
            </span>
          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>Protocol Buffer是Google的语言中立的，平台中立的，可扩展机制的，用于序列化结构化数据 - 对比XML，但更小，更快，更简单。您可以定义数据的结构化，然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。</p>
<h3 id="1-定义消息类型"><a href="#1-定义消息类型" class="headerlink" title="1. 定义消息类型"></a>1. 定义消息类型</h3><p>先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式，每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数，以及每一页多少条查询结果。可以采用如下的方式来定义消息类型的.proto文件了：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">syntax = &quot;proto3&quot;;</span><br><span class="line"></span><br><span class="line">message SearchRequest &#123;</span><br><span class="line">  string query = 1;</span><br><span class="line">  int32 page_number = 2;</span><br><span class="line">  int32 result_per_page = 3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>该文件的第一行指定您正在使用<code>proto3</code>语法：如果您不这样做，protobuf 编译器将假定您正在使用<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto" target="_blank" rel="noopener">proto2</a>。这必须是文件的第一个非空的非注释行。</li>
<li>所述<code>SearchRequest</code>消息定义指定了三个字段（名称/值对），一个用于要在此类型的消息中包含的每个数据片段。每个字段都有一个名称和类型。</li>
</ul>
<a id="more"></a>

<h5 id="1-1-指定字段类型"><a href="#1-1-指定字段类型" class="headerlink" title="1.1 指定字段类型"></a>1.1 指定字段类型</h5><p>在上面的示例中，所有字段都是<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23scalar" target="_blank" rel="noopener">标量类型</a>：两个整数（<code>page_number</code>和<code>result_per_page</code>）和一个字符串（<code>query</code>）。但是，您还可以为字段指定合成类型，包括<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23enum" target="_blank" rel="noopener">枚举</a>和其他消息类型。</p>
<h5 id="1-2-分配标识号"><a href="#1-2-分配标识号" class="headerlink" title="1.2 分配标识号"></a>1.2 分配标识号</h5><p>正如上述文件格式，在消息定义中，每个字段都有唯一的一个<strong>数字标识符</strong>。这些标识符是用来在消息的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fencoding" target="_blank" rel="noopener">二进制格式</a>中识别各个字段的，一旦开始使用就不能够再改变。注：[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记：要为将来有可能添加的、频繁出现的标识号预留一些标识号。</p>
<p>最小的标识号可以从1开始，最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000－19999]的标识号， Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号，编译时就会报警。</p>
<h5 id="1-3-指定字段规则"><a href="#1-3-指定字段规则" class="headerlink" title="1.3 指定字段规则"></a>1.3 指定字段规则</h5><p>消息字段可以是以下之一：</p>
<ul>
<li>单数：格式良好的消息可以包含该字段中的零个或一个（但不超过一个）。</li>
<li><code>repeated</code>：此字段可以在格式良好的消息中重复任意次数（包括零）。将保留重复值的顺序。在proto3中，<code>repeated</code>数字类型的字段默认使用<code>packed</code>编码。<code>packed</code>您可以在<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fencoding.html%23packed" target="_blank" rel="noopener">协议缓冲区编码中</a>找到有关编码的更多信息。</li>
</ul>
<ul>
<li><p>限定修饰符包含 required\optional\repeated</p>
<ul>
<li><p>Required: 表示是一个必须字段，必须相对于发送方，在发送消息之前必须设置该字段的值，对于接收方，必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常，导致消息被丢弃。</p>
</li>
<li><p>Optional：表示是一个可选字段，可选对于发送方，在发送消息时，可以有选择性的设置或者不设置该字段的值。对于接收方，如果能够识别可选字段就进行相应的处理，如果无法识别，则忽略该字段，消息中的其它字段正常处理。—因为optional字段的特性，很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段，这样老的版本无需升级程序也可以正常的与新的软件进行通信，只不过新的字段无法识别而已，因为并不是每个节点都需要新的功能，因此可以做到按需升级和平滑过渡。</p>
</li>
<li><p>Repeated：表示该字段可以包含0~N个元素。其特性和optional一样，但是每一次可以包含多个值。可以看作是在传递一个数组的值。</p>
</li>
</ul>
</li>
</ul>
<h5 id="1-4-添加更多消息类型"><a href="#1-4-添加更多消息类型" class="headerlink" title="1.4 添加更多消息类型"></a>1.4 添加更多消息类型</h5><p>可以在单个<code>.proto</code>文件中定义多种消息类型。如果要定义多个相关消息，这很有用  </p>
<p>例如，如果要定义与<code>SearchResponse</code>消息类型对应的回复消息格式，可以将其添加到相同的消息<code>.proto</code>：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">message SearchRequest &#123;</span><br><span class="line">  string query = 1;</span><br><span class="line">  int32 page_number = 2;</span><br><span class="line">  int32 result_per_page = 3;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message SearchResponse &#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="1-5-添加注释"><a href="#1-5-添加注释" class="headerlink" title="1.5 添加注释"></a>1.5 添加注释</h5><p>要为<code>.proto</code>文件添加注释，请使用C / C ++ - 样式<code>//</code>和<code>/* ... */</code>语法。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">/* SearchRequest表示搜索查询，带有分页选项</span><br><span class="line"> *表明响应中包含哪些结果。*/</span><br><span class="line"></span><br><span class="line">message SearchRequest &#123;</span><br><span class="line">  string query = 1;</span><br><span class="line">  int32 page_number = 2; //我们想要哪个页码？</span><br><span class="line">  int32 result_per_page = 3; //每页返回的结果数。</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="1-6-保留字段"><a href="#1-6-保留字段" class="headerlink" title="1.6 保留字段"></a>1.6 保留字段</h5><p>如果通过完全删除字段或将其注释来<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23updating" target="_blank" rel="noopener">更新</a>消息类型，则未来用户可以在对类型进行自己的更新时重用字段编号。</p>
<p>如果以后加载相同的旧版本，这可能会导致严重问题<code>.proto</code>，包括数据损坏，隐私错误等。确保不会发生这种情况的一种方法是指定已删除字段的字段编号（和/或名称，这也可能导致JSON序列化问题）<code>reserved</code>。如果将来的任何用户尝试使用这些字段标识符，协议缓冲编译器将会抱怨。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">message Foo &#123;</span><br><span class="line">  reserved 2, 15, 9 to 11;</span><br><span class="line">  reserved &quot;foo&quot;, &quot;bar&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>请注意，您不能在同一<code>reserved</code>语句中混合字段名称和字段编号。</p>
<h5 id="1-7-你的生成是什么-proto？"><a href="#1-7-你的生成是什么-proto？" class="headerlink" title="1.7 你的生成是什么.proto？"></a>1.7 你的生成是什么<code>.proto</code>？</h5><p>当您在a上运行<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23generating" target="_blank" rel="noopener">协议缓冲区编译器</a>时<code>.proto</code>，编译器会生成您所选语言的代码，您需要使用您在文件中描述的消息类型，包括获取和设置字段值，将消息序列化为输出流，并从输入流解析您的消息。</p>
<ul>
<li>对于<strong>C ++</strong>，编译器会从每个文件生成一个<code>.h</code>和一个<code>.cc</code>文件<code>.proto</code>，并为您文件中描述的每种消息类型提供一个类。</li>
<li>对于<strong>Java</strong>，编译器生成一个<code>.java</code>文件，其中包含每种消息类型的类，以及<code>Builder</code>用于创建消息类实例的特殊类。</li>
<li><strong>Python</strong>有点不同 - Python编译器生成一个模块，其中包含每个消息类型的静态描述符，<code>.proto</code>然后与<em>元类</em>一起使用，以在运行时创建必要的Python数据访问类。</li>
<li>对于<strong>Go</strong>，编译器会为<code>.pb.go</code>文件中的每种消息类型生成一个类型的文件。</li>
<li>对于<strong>Ruby</strong>，编译器生成一个<code>.rb</code>包含消息类型的Ruby模块的文件。</li>
<li>对于<strong>Objective-C</strong>，编译器从每个文件生成一个<code>pbobjc.h</code>和一个<code>pbobjc.m</code>文件<code>.proto</code>，其中包含文件中描述的每种消息类型的类。</li>
<li>对于<strong>C＃</strong>，编译器会<code>.cs</code>从每个文件生成一个文件<code>.proto</code>，其中包含文件中描述的每种消息类型的类。</li>
</ul>
<p>您可以按照所选语言的教程（即将推出的proto3版本）了解有关为每种语言使用API的更多信息。有关更多API详细信息，请参阅相关<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">API参考</a>（proto3版本即将推出）。</p>
<h3 id="2-标量值类型"><a href="#2-标量值类型" class="headerlink" title="2. 标量值类型"></a>2. 标量值类型</h3><p>标量消息字段可以具有以下类型之一 - 该表显示<code>.proto</code>文件中指定的类型，以及自动生成的类中的相应类型：</p>
<table>
<thead>
<tr>
<th>.proto type</th>
<th align="left">notes</th>
<th>C ++ type</th>
<th>Java type</th>
<th>Python type [2]</th>
<th>Go type</th>
<th>Ruby type</th>
<th>C# type</th>
<th>PHP type</th>
</tr>
</thead>
<tbody><tr>
<td>double</td>
<td align="left"></td>
<td>double</td>
<td>double</td>
<td>float</td>
<td>float64</td>
<td>float</td>
<td>double</td>
<td>float</td>
</tr>
<tr>
<td>float</td>
<td align="left"></td>
<td>float</td>
<td>float</td>
<td>float</td>
<td>FLOAT32</td>
<td>float</td>
<td>float</td>
<td>float</td>
</tr>
<tr>
<td>INT32</td>
<td align="left">使用可变长度编码。编码负数的效率低 - 如果您的字段可能有负值，请改用sint32。</td>
<td>INT32</td>
<td>INT</td>
<td>INT</td>
<td>INT32</td>
<td>Fixnum or Bignum (as needed)</td>
<td>INT</td>
<td>Integer</td>
</tr>
<tr>
<td>Int64</td>
<td align="left">使用可变长度编码。编码负数的效率低 - 如果您的字段可能有负值，请改用sint64。</td>
<td>Int64</td>
<td>long</td>
<td>int / long [3]</td>
<td>Int64</td>
<td>TWINS</td>
<td>long</td>
<td>Integer/string[5]</td>
</tr>
<tr>
<td>UINT32</td>
<td align="left">使用可变长度编码。</td>
<td>UINT32</td>
<td>int [1]</td>
<td>int / long [3]</td>
<td>UINT32</td>
<td>Fixnum or Bignum (as needed)</td>
<td>UINT</td>
<td>Integer</td>
</tr>
<tr>
<td>UINT64</td>
<td align="left">使用可变长度编码。</td>
<td>UINT64</td>
<td>Long [1]</td>
<td>int / long [3]</td>
<td>UINT64</td>
<td>TWINS</td>
<td>ULONG</td>
<td>Integer/string[5]</td>
</tr>
<tr>
<td>SINT32</td>
<td align="left">使用可变长度编码。签名的int值。这些比常规int32更有效地编码负数。</td>
<td>INT32</td>
<td>INT</td>
<td>INT</td>
<td>INT32</td>
<td>Fixnum or Bignum (as needed)</td>
<td>INT</td>
<td>Integer</td>
</tr>
<tr>
<td>sint64</td>
<td align="left">使用可变长度编码。签名的int值。这些比常规int64更有效地编码负数。</td>
<td>Int64</td>
<td>long</td>
<td>int / long [3]</td>
<td>Int64</td>
<td>TWINS</td>
<td>long</td>
<td>Integer/string[5]</td>
</tr>
<tr>
<td>fixed32</td>
<td align="left">总是四个字节。如果值通常大于2 28，则比uint32更有效。</td>
<td>UINT32</td>
<td>int [1]</td>
<td>int / long [3]</td>
<td>UINT32</td>
<td>Fixnum or Bignum (as needed)</td>
<td>UINT</td>
<td>Integer</td>
</tr>
<tr>
<td>fixed64</td>
<td align="left">总是八个字节。如果值通常大于2 56，则比uint64更有效。</td>
<td>UINT64</td>
<td>Long [1]</td>
<td>int / long [3]</td>
<td>UINT64</td>
<td>TWINS</td>
<td>ULONG</td>
<td>Integer/string[5]</td>
</tr>
<tr>
<td>sfixed32</td>
<td align="left">总是四个字节。</td>
<td>INT32</td>
<td>INT</td>
<td>INT</td>
<td>INT32</td>
<td>Fixnum or Bignum (as needed)</td>
<td>INT</td>
<td>Integer</td>
</tr>
<tr>
<td>sfixed64</td>
<td align="left">总是八个字节。</td>
<td>Int64</td>
<td>long</td>
<td>int / long [3]</td>
<td>Int64</td>
<td>TWINS</td>
<td>long</td>
<td>Integer/string[5]</td>
</tr>
<tr>
<td>Boolean</td>
<td align="left"></td>
<td>Boolean</td>
<td>Boolean</td>
<td>Boolean</td>
<td>Boolean</td>
<td>TrueClass / FalseClass</td>
<td>Boolean</td>
<td>Boolean</td>
</tr>
<tr>
<td>string</td>
<td align="left">字符串必须始终包含UTF-8编码或7位ASCII文本。</td>
<td>string</td>
<td>string</td>
<td>str / unicode[4]</td>
<td>string</td>
<td>String (UTF-8)</td>
<td>string</td>
<td>string</td>
</tr>
<tr>
<td>byte</td>
<td align="left">可以包含任意字节序列。</td>
<td>string</td>
<td>Byte string</td>
<td>Strait</td>
<td>[]byte</td>
<td>String (ASCII-8BIT)</td>
<td>Byte string</td>
<td>string</td>
</tr>
</tbody></table>
<p>在<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fencoding" target="_blank" rel="noopener">协议缓冲区编码中</a>序列化消息时，您可以找到有关如何编码这些类型的更多信息。</p>
<p>[1]在Java中，无符号的32位和64位整数使用它们的带符号对应表示，最高位只是存储在符号位中。</p>
<p>[2]在所有情况下，将值设置为字段将执行类型检查以确保其有效。</p>
<p>[3] 64位或无符号32位整数在解码时始终表示为long，但如果在设置字段时给出int，则可以为int。在所有情况下，该值必须适合设置时表示的类型。见[2]。</p>
<p>[4] Python字符串在解码时表示为unicode，但如果给出了ASCII字符串，则可以是str（这可能会发生变化）。</p>
<p>[5] Integer用于64位计算机，字符串用于32位计算机。</p>
<h3 id="3-默认值"><a href="#3-默认值" class="headerlink" title="3. 默认值"></a>3. 默认值</h3><p>解析消息时，如果编码消息不包含特定的单数元素，则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的：</p>
<ul>
<li>对于字符串，默认值为空字符串。</li>
<li>对于字节，默认值为空字节。</li>
<li>对于bools，默认值为false。</li>
<li>对于数字类型，默认值为零。</li>
<li>对于<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23enum" target="_blank" rel="noopener">枚举</a>，默认值是第<strong>一个定义的枚举值</strong>，该<strong>值</strong>必须为0。</li>
<li>对于消息字段，未设置该字段。它的确切值取决于语言。有关详细信息， 请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">生成的代码指</a></li>
</ul>
<p>重复字段的默认值为空（通常是相应语言的空列表）。</p>
<p>请注意，对于标量消息字段，一旦解析了消息，就无法确定字段是否显式设置为默认值（例如，是否设置了布尔值<code>false</code>）或者根本没有设置：您应该记住这一点在定义消息类型时。例如，<code>false</code>如果您不希望默认情况下也发生这种行为，那么在设置为时，没有一个布尔值可以启用某些行为。还要注意的是，如果一个标消息字段<strong>被</strong>设置为默认值，该值将不会在电线上连载。</p>
<p>有关默认值如何在生成的代码中工作的更多详细信息，请参阅所选语言的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">生成代码指南</a>。</p>
<h3 id="4-枚举"><a href="#4-枚举" class="headerlink" title="4. 枚举"></a>4. 枚举</h3><p>在定义消息类型时，您可能希望其中一个字段只有一个预定义的值列表。例如，假设你想添加一个 <code>corpus</code>字段每个<code>SearchRequest</code>，其中语料库可以 <code>UNIVERSAL</code>，<code>WEB</code>，<code>IMAGES</code>，<code>LOCAL</code>，<code>NEWS</code>，<code>PRODUCTS</code>或<code>VIDEO</code>。您可以非常简单地通过<code>enum</code>为每个可能的值添加一个常量来定义消息定义。</p>
<p>在下面的示例中，我们添加了一个带有所有可能值的<code>enum</code>调用<code>Corpus</code>，以及一个类型的字段<code>Corpus</code>：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">message SearchRequest &#123;</span><br><span class="line">  string query = 1;</span><br><span class="line">  int32 page_number = 2;</span><br><span class="line">  int32 result_per_page = 3;</span><br><span class="line">  enum Corpus &#123;</span><br><span class="line">    UNIVERSAL = 0;</span><br><span class="line">    WEB = 1;</span><br><span class="line">    IMAGES = 2;</span><br><span class="line">    LOCAL = 3;</span><br><span class="line">    NEWS = 4;</span><br><span class="line">    PRODUCTS = 5;</span><br><span class="line">    VIDEO = 6;</span><br><span class="line">  &#125;</span><br><span class="line">  Corpus corpus = 4;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如您所见，<code>Corpus</code>枚举的第一个常量映射为零：每个枚举定义<strong>必须</strong>包含一个映射到零的常量作为其第一个元素。这是因为：</p>
<ul>
<li>必须有一个零值，以便我们可以使用0作为数字<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23default" target="_blank" rel="noopener">默认值</a>。</li>
<li>零值必须是第一个元素，以便与<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto" target="_blank" rel="noopener">proto2</a>语义兼容，其中第一个枚举值始终是默认值。</li>
</ul>
<p>您可以通过为不同的枚举常量指定相同的值来定义别名。为此，您需要将<code>allow_alias</code>选项设置为<code>true</code>，否则协议编译器将在找到别名时生成错误消息。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">enum EnumAllowingAlias &#123;</span><br><span class="line">  option allow_alias = true;</span><br><span class="line">  UNKNOWN = 0;</span><br><span class="line">  STARTED = 1;</span><br><span class="line">  RUNNING = 1;</span><br><span class="line">&#125;</span><br><span class="line">enum EnumNotAllowingAlias &#123;</span><br><span class="line">  UNKNOWN = 0;</span><br><span class="line">  STARTED = 1;</span><br><span class="line">  // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>枚举器常量必须在32位整数范围内。由于<code>enum</code>值在线上使用<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fencoding" target="_blank" rel="noopener">varint编码</a>，因此负值效率低，因此不建议使用。您可以<code>enum</code>在消息定义中定义s，如上例所示，<code>enum</code>也可以在外部定义 - 这些可以在<code>.proto</code>文件的任何消息定义中重用。您还可以使用<code>enum</code>语法将一个消息中声明的类型用作另一个消息中的字段类型。 <code>*MessageType*.*EnumType*</code></p>
<p>当你在<code>.proto</code>使用a的协议缓冲编译器上运行时<code>enum</code>，生成的代码将具有<code>enum</code>Java或C ++ 的相应代码，这<code>EnumDescriptor</code>是Python的一个特殊类，用于在运行时生成的类中创建一组带有整数值的符号常量。</p>
<p>在反序列化期间，将在消息中保留无法识别的枚举值，但是当反序列化消息时，如何表示这种值取决于语言。在支持具有超出指定符号范围的值的开放枚举类型的语言中，例如C ++和Go，未知的枚举值仅作为其基础整数表示存储。在具有封闭枚举类型（如Java）的语言中，枚举中的大小写用于表示无法识别的值，并且可以使用特殊访问器访问基础整数。在任何一种情况下，如果消息被序列化，则仍然会使用消息序列化无法识别的值。</p>
<p>有关如何<code>enum</code>在应用程序中使用消息的详细信息，请参阅所选语言的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">生成代码指南</a>。</p>
<h5 id="4-1-保留值"><a href="#4-1-保留值" class="headerlink" title="4.1 保留值"></a>4.1 保留值</h5><p>如果通过完全删除枚举条目或将其注释掉来<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23updating" target="_blank" rel="noopener">更新</a>枚举类型，则未来用户可以在对类型进行自己的更新时重用该数值。如果以后加载相同的旧版本，这可能会导致严重问题<code>.proto</code>，包括数据损坏，隐私错误等。确保不会发生这种情况的一种方法是指定已删除条目的数值（和/或名称，这也可能导致JSON序列化问题）<code>reserved</code>。如果将来的任何用户尝试使用这些标识符，协议缓冲编译器将会抱怨。您可以使用<code>max</code>关键字指定保留的数值范围达到最大可能值。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">enum Foo &#123;</span><br><span class="line">  reserved 2, 15, 9 to 11, 40 to max;</span><br><span class="line">  reserved &quot;FOO&quot;, &quot;BAR&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>请注意，您不能在同一<code>reserved</code>语句中混合字段名称和数值。</p>
<h3 id="5-使用其他消息类型"><a href="#5-使用其他消息类型" class="headerlink" title="5. 使用其他消息类型"></a>5. 使用其他消息类型</h3><p>您可以使用其他消息类型作为字段类型。例如，假设你想包括<code>Result</code>每个消息的<code>SearchResponse</code>消息-要做到这一点，你可以定义一个<code>Result</code>在同一个消息类型<code>.proto</code>，然后指定类型的字段<code>Result</code>中<code>SearchResponse</code>：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">message SearchResponse &#123;</span><br><span class="line">  repeated Result results = 1;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">message Result &#123;</span><br><span class="line">  string url = 1;</span><br><span class="line">  string title = 2;</span><br><span class="line">  repeated string snippets = 3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="5-1-导入定义"><a href="#5-1-导入定义" class="headerlink" title="5.1 导入定义"></a>5.1 导入定义</h5><p>在上面的示例中，<code>Result</code>消息类型在同一文件中定义<code>SearchResponse</code>- 如果要用作字段类型的消息类型已在另一个<code>.proto</code>文件中定义，该怎么办？</p>
<p>您可以<code>.proto</code>通过<em>导入</em>来使用其他文件中的定义。要导入其他<code>.proto</code>人的定义，请在文件顶部添加import语句：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">"myproject/other_protos.proto"</span>;</span><br></pre></td></tr></table></figure>

<p>默认情况下，您只能使用直接导入<code>.proto</code>文件中的定义。但是，有时您可能需要将<code>.proto</code>文件移动到新位置。<code>.proto</code>现在，您可以<code>.proto</code>在旧位置放置一个虚拟文件，以使用该<code>import public</code>概念将所有导入转发到新位置，而不是直接移动文件并在一次更改中更新所有调用站点。<code>import public</code>任何导入包含该<code>import public</code>语句的proto的人都可以传递依赖关系。例如：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// new.proto</span></span><br><span class="line"><span class="comment">// All definitions are moved here</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// old.proto</span></span><br><span class="line"><span class="comment">//This is the proto that all clients are importing.</span></span><br><span class="line"><span class="keyword">import</span> public“new.proto”;</span><br><span class="line"><span class="keyword">import</span>“other.proto”;</span><br><span class="line"></span><br><span class="line"><span class="comment">// client.proto</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"old.proto"</span>;</span><br><span class="line"><span class="comment">//您使用old.proto和new.proto中的定义，但不使用other.proto</span></span><br></pre></td></tr></table></figure>

<p>协议编译器使用<code>-I</code>/ <code>--proto_path</code>flag 在协议编译器命令行中指定的一组目录中搜索导入的文件 。如果没有给出标志，它将查找调用编译器的目录。通常，您应该将<code>--proto_path</code>标志设置为项目的根目录，并对所有导入使用完全限定名称。</p>
<h5 id="5-2-使用proto2消息类型"><a href="#5-2-使用proto2消息类型" class="headerlink" title="5.2 使用proto2消息类型"></a>5.2 使用proto2消息类型</h5><p>可以导入<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto" target="_blank" rel="noopener">proto2</a>消息类型并在proto3消息中使用它们，反之亦然。但是，proto2枚举不能直接用于proto3语法（如果导入的proto2消息使用它们就可以了）。</p>
<h3 id="6-嵌套类型"><a href="#6-嵌套类型" class="headerlink" title="6. 嵌套类型"></a>6. 嵌套类型</h3><p>您可以在其他消息类型中定义和使用消息类型，如下例所示 - 此处<code>Result</code>消息在消息中定义<code>SearchResponse</code>：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">message SearchResponse &#123;</span><br><span class="line">  message Result &#123;</span><br><span class="line">    string url = 1;</span><br><span class="line">    string title = 2;</span><br><span class="line">    repeated string snippets = 3;</span><br><span class="line">  &#125;</span><br><span class="line">  repeated Result results = 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果要在其父消息类型之外重用此消息类型，请将其称为： <code>*Parent*.*Type*</code></p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">message SomeOtherMessage &#123;</span><br><span class="line">  SearchResponse.Result result = 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>您可以根据需要深入嵌套消息：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">message Outer &#123;       // Level 0</span><br><span class="line">  message MiddleAA &#123;  // Level 1</span><br><span class="line">    message Inner &#123;   // Level 2</span><br><span class="line">      int64 ival = 1;</span><br><span class="line">      bool  booly = 2;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  message MiddleBB &#123;  // Level 1</span><br><span class="line">    message Inner &#123;   // Level 2</span><br><span class="line">      int32 ival = 1;</span><br><span class="line">      bool  booly = 2;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="7-更新消息类型"><a href="#7-更新消息类型" class="headerlink" title="7. 更新消息类型"></a>7. 更新消息类型</h3><p>如果现有的消息类型不再满足您的所有需求 - 例如，您希望消息格式具有额外的字段 - 但您仍然希望使用使用旧格式创建的代码，请不要担心！在不破坏任何现有代码的情况下更新消息类型非常简单。请记住以下规则：</p>
<ul>
<li>请勿更改任何现有字段的字段编号。</li>
<li>如果添加新字段，则使用“旧”消息格式按代码序列化的任何消息仍可由新生成的代码进行解析。您应该记住这些元素的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23default" target="_blank" rel="noopener">默认值</a>，以便新代码可以正确地与旧代码生成的消息进行交互。同样，您的新代码创建的消息可以由旧代码解析：旧的二进制文件在解析时只是忽略新字段。有关详细信息，请参阅“ <a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23unknowns" target="_blank" rel="noopener">未知字段”</a>部分</li>
<li>只要在更新的消息类型中不再使用字段编号，就可以删除字段。您可能希望重命名该字段，可能添加前缀“OBSOLETE_”，或者<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23reserved" target="_blank" rel="noopener">保留</a>字段编号，以便您的未来用户<code>.proto</code>不会意外地重复使用该编号。</li>
<li><code>int32</code>，<code>uint32</code>，<code>int64</code>，<code>uint64</code>，和<code>bool</code>都是兼容的-这意味着你可以改变这些类型到另一个的一个场不破坏forwards-或向后兼容。如果从导线中解析出一个不符合相应类型的数字，您将获得与在C ++中将该数字转换为该类型相同的效果（例如，如果将64位数字作为int32读取，它将被截断为32位）。</li>
<li><code>sint32</code>并且<code>sint64</code>彼此兼容但与其他整数类型<em>不</em>兼容。</li>
<li><code>string``bytes</code>只要字节是有效的UTF-8 ，它们是兼容的。</li>
<li><code>bytes</code>如果字节包含消息的编码版本，则嵌入消息是兼容的。</li>
<li><code>fixed32</code>与兼容<code>sfixed32</code>，并<code>fixed64</code>用<code>sfixed64</code>。</li>
<li><code>enum</code>与兼容<code>int32</code>，<code>uint32</code>，<code>int64</code>，和<code>uint64</code>电线格式条款（注意，如果他们不适合的值将被截断）。但请注意，在反序列化消息时，客户端代码可能会以不同方式对待它们：例如，<code>enum</code>将在消息中保留未识别的proto3 类型，但在反序列化消息时如何表示这种类型取决于语言。Int字段总是保留它们的价值。</li>
<li>将单个值更改为<strong>新</strong> 成员<code>oneof</code>是安全且二进制兼容的。<code>oneof</code>如果您确定没有代码一次设置多个字段，则将多个字段移动到新字段可能是安全的。将任何字段移动到现有字段<code>oneof</code>并不安全。</li>
</ul>
<h3 id="8-未知字段"><a href="#8-未知字段" class="headerlink" title="8. 未知字段"></a>8. 未知字段</h3><p>未知字段是格式良好的协议缓冲区序列化数据，表示解析器无法识别的字段。例如，当旧二进制文件解析具有新字段的新二进制文件发送的数据时，这些新字段将成为旧二进制文件中的未知字段。</p>
<p>最初，proto3消息在解析期间总是丢弃未知字段，但在3.5版本中，我们重新引入了保存未知字段以匹配proto2行为。在版本3.5及更高版本中，未知字段在解析期间保留并包含在序列化输出中。</p>
<h3 id="9-任何"><a href="#9-任何" class="headerlink" title="9. 任何"></a>9. 任何</h3><p>该<code>Any</code>消息类型，可以使用邮件作为嵌入式类型，而不必自己.proto定义。一个<code>Any</code>含有任意的序列化消息<code>bytes</code>，以充当一个全局唯一标识符和解析到该消息的类型的URL一起。要使用该<code>Any</code>类型，您需要<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23other" target="_blank" rel="noopener">导入</a><code>google/protobuf/any.proto</code>。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">import &quot;google/protobuf/any.proto&quot;;</span><br><span class="line"></span><br><span class="line">message ErrorStatus &#123;</span><br><span class="line">  string message = 1;</span><br><span class="line">  repeated google.protobuf.Any details = 2;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>给定消息类型的默认类型URL是。 <code>type.googleapis.com/*packagename*.*messagename*</code></p>
<p>不同的语言实现将支持运行时库佣工类型安全的方式打包和解包的任何值-例如，在Java中，任何类型都会有特殊<code>pack()</code>和<code>unpack()</code>存取，而在C ++中有<code>PackFrom()</code>和<code>UnpackTo()</code>方法：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Storing an arbitrary message type in Any.</span></span><br><span class="line">NetworkErrorDetails details = ...;</span><br><span class="line">ErrorStatus status;</span><br><span class="line">status.add_details()-&gt;PackFrom(details);</span><br><span class="line"></span><br><span class="line">// Reading an arbitrary message from Any.</span><br><span class="line">ErrorStatus status = ...;</span><br><span class="line">for (const Any&amp; detail : status.details()) &#123;</span><br><span class="line">  if (detail.Is&lt;NetworkErrorDetails&gt;()) &#123;</span><br><span class="line">    NetworkErrorDetails network_error;</span><br><span class="line">    detail.UnpackTo(&amp;network_error);</span><br><span class="line">    ... processing network_error ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>目前，正在开发用于处理Any类型的运行时库</strong>。</p>
<p>如果您已熟悉<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto" target="_blank" rel="noopener">proto2语法</a>，则Any类型将替换<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto%23extensions" target="_blank" rel="noopener">扩展</a>。</p>
<h3 id="10-Oneof"><a href="#10-Oneof" class="headerlink" title="10. Oneof"></a>10. Oneof</h3><p>如果您有一个包含许多字段的消息，并且最多只能同时设置一个字段，则可以使用oneof功能强制执行此行为并节省内存。</p>
<p>除了一个共享内存中的所有字段之外，其中一个字段类似于常规字段，并且最多可以同时设置一个字段。设置oneof的任何成员会自动清除所有其他成员。您可以使用特殊<code>case()</code>或<code>WhichOneof()</code>方法检查oneof中的哪个值（如果有），具体取决于您选择的语言。</p>
<h5 id="10-1-使用Oneof"><a href="#10-1-使用Oneof" class="headerlink" title="10.1 使用Oneof"></a>10.1 使用Oneof</h5><p>要在您中定义oneof，请<code>.proto</code>使用<code>oneof</code>关键字后跟您的oneof名称，在这种情况下<code>test_oneof</code>：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">message SampleMessage &#123;</span><br><span class="line">  oneof test_oneof &#123;</span><br><span class="line">    string name = 4;</span><br><span class="line">    SubMessage sub_message = 9;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后，将oneof字段添加到oneof定义中。您可以添加任何类型的字段，但不能使用<code>repeated</code>字段。</p>
<p>在生成的代码中，oneof字段与常规字段具有相同的getter和setter。您还可以使用特殊方法检查oneof中的值（如果有）。您可以在相关<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">API参考中</a>找到有关所选语言的oneof API的更多信息。</p>
<h5 id="10-2-Oneof-特性"><a href="#10-2-Oneof-特性" class="headerlink" title="10.2 Oneof 特性"></a>10.2 Oneof 特性</h5><ul>
<li><p>设置oneof字段将自动清除oneof的所有其他成员。因此，如果您设置了多个字段，则只有您设置的最后一个字段仍然具有值。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">SampleMessage message;</span><br><span class="line">  message.set_name("name");</span><br><span class="line">CHECK(message.has_name());</span><br><span class="line">  message.mutable_sub_message();   // Will clear name field.</span><br><span class="line">  CHECK(!message.has_name());</span><br></pre></td></tr></table></figure>


</li>
</ul>
<ul>
<li><p>如果解析器在线路上遇到同一个oneof的多个成员，则在解析的消息中仅使用看到的最后一个成员。</p>
</li>
<li><p>oneof不支持<code>repeated</code>。</p>
</li>
<li><p>Reflection API适用于其中一个字段。</p>
</li>
<li><p>如果您使用的是C ++，请确保您的代码不会导致内存崩溃。以下示例代码将崩溃，<code>sub_message</code>已通过调用该<code>set_name()</code>方法删除了该代码。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">SampleMessage message;</span><br><span class="line">  SubMessage* sub_message = message.mutable_sub_message();</span><br><span class="line">  message.set_name(&quot;name&quot;);      // Will delete sub_message</span><br><span class="line">  sub_message-&gt;set_...            // Crashes here</span><br></pre></td></tr></table></figure>


</li>
</ul>
<ul>
<li><p>同样在C ++中，如果你有<code>Swap()</code>两个消息与oneofs，每个消息最终将与另一个消息结果：在下面的例子中，<code>msg1</code>将有一个<code>sub_message</code>，<code>msg2</code>并将有一<code>name</code>。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">SampleMessage msg1;</span><br><span class="line">msg1.set_name("name");</span><br><span class="line">SampleMessage msg2;</span><br><span class="line">msg2.mutable_sub_message();</span><br><span class="line">msg1.swap(&amp;msg2);</span><br><span class="line">CHECK(msg1.has_sub_message());</span><br><span class="line">CHECK(msg2.has_name());</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h5 id="10-3-向后兼容性问题"><a href="#10-3-向后兼容性问题" class="headerlink" title="10.3 向后兼容性问题"></a>10.3 向后兼容性问题</h5><p>添加或删除其中一个字段时要小心。如果检查oneof返回的值<code>None</code>/ <code>NOT_SET</code>，这可能意味着oneof尚未设置或已在不同版本的oneof的被设置为一个字段。没有办法区分，因为没有办法知道线上的未知字段是否是其中一个成员。</p>
<h5 id="10-4-标签重用问题"><a href="#10-4-标签重用问题" class="headerlink" title="10.4 标签重用问题"></a>10.4 标签重用问题</h5><ul>
<li><strong>将字段移入或移出oneof</strong>：在序列化和解析消息后，您可能会丢失一些信息（某些字段将被清除）。但是，您可以安全地将单个字段移动到<strong>新的</strong> oneof中，并且如果已知只有一个字段被设置，则可以移动多个字段。</li>
<li><strong>删除oneof字段并将其添加回</strong>：在序列化和解析消息后，这可能会清除当前设置的oneof字段。</li>
<li><strong>拆分或合并oneof</strong>：这与移动常规字段有类似的问题。</li>
</ul>
<h3 id="11-地图"><a href="#11-地图" class="headerlink" title="11. 地图"></a>11. 地图</h3><p>如果要在数据定义中创建关联映射，协议缓冲区提供了一种方便的快捷方式语法：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">map &lt; key_type ，value_type &gt; map_field = N ;</span><br></pre></td></tr></table></figure>

<p>…其中<code>key_type</code>可以是任何整数或字符串类型（因此，除了浮点类型之外的任何<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23scalar" target="_blank" rel="noopener">标量</a>类型<code>bytes</code>）。请注意，枚举不是有效的<code>key_type</code>。的<code>value_type</code>可以是任何类型的除另一地图。</p>
<p>因此，例如，如果要创建项目映射，其中每条<code>Project</code>消息都与字符串键相关联，则可以像下面这样定义它：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">map &lt; <span class="built_in">string</span> ，Project &gt; projects = <span class="number">3</span> ;</span><br></pre></td></tr></table></figure>

<ul>
<li>地图字段不能<code>repeated</code>。</li>
<li>地图值的有线格式排序和地图迭代排序未定义，因此您不能依赖于特定顺序的地图项目。</li>
<li>为a生成文本格式时<code>.proto</code>，地图按键排序。数字键按数字排序。</li>
<li>从线路解析或合并时，如果有重复的映射键，则使用最后看到的键。从文本格式解析映射时，如果存在重复键，则解析可能会失败。</li>
<li>如果为映射字段提供键但没有值，则字段序列化时的行为取决于语言。在C ++，Java和Python中，类型的默认值是序列化的，而在其他语言中没有任何序列化。</li>
</ul>
<p>生成的地图API目前可用于所有proto3支持的语言。您可以在相关<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Foverview" target="_blank" rel="noopener">API参考中</a>找到有关所选语言的地图API的更多信息。</p>
<h5 id="11-1-向后兼容性"><a href="#11-1-向后兼容性" class="headerlink" title="11.1 向后兼容性"></a>11.1 向后兼容性</h5><p>映射语法在线上等效于以下内容，因此不支持映射的协议缓冲区实现仍可处理您的数据：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">message MapFieldEntry &#123;</span><br><span class="line">  key_type key = 1;</span><br><span class="line">  value_type value = 2;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">repeated MapFieldEntry map_field = N;</span><br></pre></td></tr></table></figure>

<p>任何支持映射的协议缓冲区实现都必须生成和接受上述定义可以接受的数据。</p>
<h3 id="12-包"><a href="#12-包" class="headerlink" title="12. 包"></a>12. 包</h3><p>您可以向<code>.proto</code>文件添加<code>package</code>可选说明符，以防止协议消息类型之间的名称冲突。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">package foo.bar;</span><br><span class="line">message Open &#123; ... &#125;</span><br></pre></td></tr></table></figure>

<p>然后，您可以在定义消息类型的字段时使用包说明符：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">message Foo &#123;</span><br><span class="line">  ...</span><br><span class="line">  foo.bar.Open open = 1;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>包说明符影响生成的代码的方式取决于您选择的语言：</p>
<ul>
<li>在<strong>C ++中</strong>，生成的类包含在C ++命名空间中。例如，<code>Open</code>将在命名空间中<code>foo::bar</code>。</li>
<li>在<strong>Java中</strong>，该包用作Java包，除非您<code>option java_package</code>在<code>.proto</code>文件中明确提供了该包。</li>
<li>在<strong>Python中</strong>，package指令被忽略，因为Python模块是根据它们在文件系统中的位置进行组织的。</li>
<li>在<strong>Go中</strong>，该包用作Go包名称，除非您<code>option go_package</code>在<code>.proto</code>文件中明确提供。</li>
<li>在<strong>Ruby中</strong>，生成的类包含在嵌套的Ruby命名空间内，转换为所需的Ruby大写形式（首字母大写;如果第一个字符不是字母，<code>PB_</code>则前置）。例如，<code>Open</code>将在命名空间中<code>Foo::Bar</code>。</li>
<li>在<strong>C＃中</strong>，包转换为PascalCase后用作命名空间，除非您<code>option csharp_namespace</code>在<code>.proto</code>文件中明确提供。例如，<code>Open</code>将在命名空间中<code>Foo.Bar</code>。</li>
</ul>
<h5 id="12-1-包和名称解析"><a href="#12-1-包和名称解析" class="headerlink" title="12.1 包和名称解析"></a>12.1 包和名称解析</h5><p>协议缓冲区语言中的类型名称解析与C ++类似：首先搜索最里面的范围，然后搜索下一个范围，依此类推，每个包被认为是其父包的“内部”。一个领先的’。’ （例如，<code>.foo.bar.Baz</code>）意味着从最外层的范围开始。</p>
<p>protobuf 编译器通过解析导入的<code>.proto</code>文件来解析所有类型名称。每种语言的代码生成器都知道如何使用该语言引用每种类型，即使它具有不同的范围规则。</p>
<h3 id="13-定义服务"><a href="#13-定义服务" class="headerlink" title="13. 定义服务"></a>13. 定义服务</h3><p>如果要将消息类型与RPC（远程过程调用）系统一起使用，则可以在<code>.proto</code>文件中定义RPC服务接口，protobuf 编译器将使用您选择的语言生成服务接口代码和存根。因此，例如，如果要定义RPC服务请求方法为:<code>SearchRequest</code>和返回方法为:<code>SearchResponse</code>，可以<code>.proto</code>按如下方式在文件中定义它：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">service SearchService &#123;</span><br><span class="line">  rpc Search（SearchRequest）returns（SearchResponse）;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>与协议缓冲区一起使用的最简单的RPC系统是<a href="https://link.juejin.im?target=https%3A%2F%2Fgrpc.io%2F" target="_blank" rel="noopener">gRPC</a>：一种由Google开发的，平台中立的开源RPC系统。gRPC特别适用于protobuf，并允许在您的<code>.proto</code>文件中使用特殊的protobuf 编译器插件直接生成相关的RPC代码。</p>
<p>如果您不想使用gRPC，也可以将protobuf与您自己的RPC实现一起使用。您可以在<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto%23services" target="_blank" rel="noopener">Proto2语言指南中</a>找到更多相关信息。</p>
<p>还有一些正在进行的第三方项目使用Protocol Buffers开发RPC实现。有关我们了解的项目的链接列表，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fgoogle%2Fprotobuf%2Fblob%2Fmaster%2Fdocs%2Fthird_party.md" target="_blank" rel="noopener">第三方加载项wiki页面</a>。</p>
<h3 id="14-JSON映射"><a href="#14-JSON映射" class="headerlink" title="14. JSON映射"></a>14. JSON映射</h3><p>Proto3支持JSON中的规范编码，使得在系统之间共享数据变得更加容易。在下表中逐个类型地描述编码。</p>
<p>如果JSON编码数据中缺少值<code>null</code>，或者其值为，则在解析为协议缓冲区时，它将被解释为适当的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto3%23default" target="_blank" rel="noopener">默认值</a>。如果字段在协议缓冲区中具有默认值，则默认情况下将在JSON编码数据中省略该字段以节省空间。实现可以提供用于在JSON编码的输出中发出具有默认值的字段的选项。</p>
<table>
<thead>
<tr>
<th>proto3</th>
<th>JSON</th>
<th>JSON示例</th>
<th>笔记</th>
</tr>
</thead>
<tbody><tr>
<td>message</td>
<td>object</td>
<td><code>{&quot;fooBar&quot;: v, &quot;g&quot;: null,…}</code></td>
<td>生成JSON对象。消息字段名称映射到小写驼峰并成为JSON对象键。如果<code>json_name</code>指定了field选项，则指定的值将用作键。解析器接受小写驼峰名称（或<code>json_name</code>选项指定的名称）和原始proto字段名称。<code>null</code>是所有字段类型的可接受值，并将其视为相应字段类型的默认值。</td>
</tr>
<tr>
<td>eunm</td>
<td>String</td>
<td><code>&quot;FOO_BAR&quot;</code></td>
<td>使用proto中指定的枚举值的名称。解析器接受枚举名称和整数值。</td>
</tr>
<tr>
<td>map&lt;K，V&gt;</td>
<td>object</td>
<td><code>{&quot;k&quot;: v, …}</code></td>
<td>所有键都转换为字符串。</td>
</tr>
<tr>
<td>repeated V.</td>
<td>array</td>
<td><code>[v, …]</code></td>
<td><code>null</code> 被接受为空列表[]。</td>
</tr>
<tr>
<td>bool</td>
<td>true,false</td>
<td><code>true, false</code></td>
<td></td>
</tr>
<tr>
<td>string</td>
<td>string</td>
<td><code>&quot;Hello World!&quot;</code></td>
<td></td>
</tr>
<tr>
<td>bytes</td>
<td>base64 string</td>
<td><code>&quot;YWJjMTIzIT8kKiYoKSctPUB+&quot;</code></td>
<td>JSON值将是使用带填充的标准base64编码编码为字符串的数据。接受带有/不带填充的标准或URL安全base64编码。</td>
</tr>
<tr>
<td>int32，fixed32，uint32</td>
<td>string</td>
<td><code>1, -10, 0</code></td>
<td>JSON值将是十进制数。接受数字或字符串。</td>
</tr>
<tr>
<td>int64，fixed64，uint64</td>
<td>string</td>
<td><code>&quot;1&quot;, &quot;-10&quot;</code></td>
<td>JSON值将是十进制字符串。接受数字或字符串。</td>
</tr>
<tr>
<td>float,double</td>
<td>number</td>
<td><code>1.1, -10.0, 0, &quot;NaN&quot;,&quot;Infinity&quot;</code></td>
<td>JSON值将是一个数字或一个特殊字符串值“NaN”，“Infinity”和“-Infinity”。接受数字或字符串。指数表示法也被接受。</td>
</tr>
<tr>
<td>any</td>
<td>object</td>
<td><code>{&quot;@type&quot;: &quot;url&quot;, &quot;f&quot;: v, … }</code></td>
<td>如果Any包含具有特殊JSON映射的值，则将按如下方式进行转换：。否则，该值将转换为JSON对象，并将插入该字段以指示实际的数据类型。<code>{&quot;@type&quot;: xxx, &quot;value&quot;: yyy}``&quot;@type&quot;</code></td>
</tr>
<tr>
<td>Timestamp</td>
<td>string</td>
<td><code>&quot;1972-01-01T10:00:20.021Z&quot;</code></td>
<td>使用RFC 3339，其中生成的输出将始终被Z标准化并使用0,3,6或9个小数位。也接受“Z”以外的偏移。</td>
</tr>
<tr>
<td>Duration</td>
<td>string</td>
<td><code>&quot;1.000340012s&quot;, &quot;1s&quot;</code></td>
<td>生成的输出始终包含0,3,6或9个小数位，具体取决于所需的精度，后跟后缀“s”。接受的是任何小数位（也没有），只要它们符合纳秒精度并且后缀“s”是必需的。</td>
</tr>
<tr>
<td>Struct</td>
<td><code>object</code></td>
<td><code>{ … }</code></td>
<td>任何JSON对象。见。<code>struct.proto</code></td>
</tr>
<tr>
<td>Wrapper types</td>
<td>various types</td>
<td><code>2, &quot;2&quot;, &quot;foo&quot;, true,&quot;true&quot;, null, 0, …</code></td>
<td>包装器在JSON中使用与包装基元类型相同的表示形式，除了<code>null</code>在数据转换和传输期间允许和保留的表示形式。</td>
</tr>
<tr>
<td>FieldMask</td>
<td>string</td>
<td><code>&quot;f.fooBar,h&quot;</code></td>
<td>见。<code>field_mask.proto</code></td>
</tr>
<tr>
<td>ListValue</td>
<td>array</td>
<td><code>[foo, bar, …]</code></td>
<td></td>
</tr>
<tr>
<td>Value</td>
<td>value</td>
<td></td>
<td>任何JSON值</td>
</tr>
<tr>
<td>NullValue</td>
<td>null</td>
<td></td>
<td>JSON null</td>
</tr>
</tbody></table>
<h5 id="13-1-JSON选项"><a href="#13-1-JSON选项" class="headerlink" title="13.1 JSON选项"></a>13.1 JSON选项</h5><p>proto3  JSON实现可以提供以下选项：</p>
<ul>
<li><strong>使用默认值发出字段</strong>：默认情况下，proto3 JSON输出中省略了<strong>具有默认值的</strong>字段。实现可以提供覆盖此行为的选项，并使用其默认值输出字段。</li>
<li><strong>忽略未知字段</strong>：默认情况下，Proto3 JSON解析器应拒绝未知字段，但可以提供忽略解析中未知字段的选项。</li>
<li><strong>使用proto字段名称而不是小写驼峰名称</strong>：默认情况下，proto3 JSON打印机应将字段名称转换为小写驼峰并将其用作JSON名称。实现可以提供使用proto字段名称作为JSON名称的选项。Proto3 JSON解析器需要接受转换后的小写驼峰名称和proto字段名称。</li>
<li><strong>将枚举值发送为整数而不是字符串</strong>：默认情况下，在JSON输出中使用枚举值的名称。可以提供选项以使用枚举值的数值。</li>
</ul>
<h3 id="14-选项"><a href="#14-选项" class="headerlink" title="14. 选项"></a>14. 选项</h3><p><code>.proto</code>文件中的各个声明可以使用许多<em>选项</em>进行注释。选项不会更改声明的整体含义，但可能会影响在特定上下文中处理它的方式。可用选项的完整列表在中定义<code>google/protobuf/descriptor.proto</code>。</p>
<p>一些选项是文件级选项，这意味着它们应该在顶级范围内编写，而不是在任何消息，枚举或服务定义中。一些选项是消息级选项，这意味着它们应该写在消息定义中。一些选项是字段级选项，这意味着它们应该写在字段定义中。选项也可以写在枚举类型，枚举值，服务类型和服务方法上; 但是，目前没有任何有用的选择。</p>
<p>以下是一些最常用的选项：</p>
<ul>
<li><p><code>java_package</code>（文件选项）：用于生成的Java类的包。如果<code>.proto</code>文件中没有给出显式选项<code>java_package</code>，则默认情况下将使用proto包（使用文件中的“package”关键字指定  .proto  ）。但是，proto包通常不能生成好的Java包，因为proto包不会以反向域名开头。如果不生成Java代码，则此选项无效。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">option</span> java_package =“com.example.foo”;</span><br></pre></td></tr></table></figure>


</li>
</ul>
<ul>
<li><code>java_multiple_files</code> （文件选项）：导致在包级别定义顶级消息，枚举和服务，而不是在.proto文件之后命名的外部类中。</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">option</span> java_multiple_files = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>

<ul>
<li><code>java_outer_classname</code>（file option）：要生成的最外层Java类（以及文件名）的类名。如果  <code>.proto</code>文件中没有指定 <code>java_outer_classname</code>，则通过将<code>.proto</code>文件名转换为驼峰格式（因此 <code>foo_bar.proto</code> 成为<code>FooBar.java</code>）来构造类名。如果不生成Java代码，则此选项无效。</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">option</span> java_outer_classname =“Ponycopter”;</span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>optimize_for</code></p>
<p>（文件选项）：可以设置为<code>SPEED</code>，<code>CODE_SIZE</code>或<code>LITE_RUNTIME</code>。这会以下列方式影响C ++和Java代码生成器（可能还有第三方生成器）：</p>
<ul>
<li><code>SPEED</code>（默认值）：protobuf 编译器将生成用于对消息类型进行序列化，解析和执行其他常见操作的代码。此代码经过高度优化。</li>
<li><code>CODE_SIZE</code>：protobuf 编译器将生成最少的类，并依赖于基于反射的共享代码来实现序列化，解析和各种其他操作。因此生成的代码比使用<code>SPEED</code>小得多，但操作会更慢。类仍将实现与<code>SPEED</code>模式完全相同的公共API 。此模式在包含非常大数量的<code>.proto</code>文件的应用程序中最有用，并且不需要所有文件都非常快速。</li>
<li><code>LITE_RUNTIME</code>：protobuf 编译器将生成仅依赖于“lite”运行时库（<code>libprotobuf-lite</code>而不是<code>libprotobuf</code>）的类。精简版运行时比整个库小得多（大约小一个数量级），但省略了描述符和反射等特定功能。这对于在移动电话等受限平台上运行的应用程序尤其有用。编译器仍然会像在<code>SPEED</code>模式中一样生成所有方法的快速实现。生成的类将仅实现<code>MessageLite</code>每种语言的接口，该接口仅提供完整<code>Message</code>接口的方法的子集。</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">option</span> optimize_for = CODE_SIZE;</span><br></pre></td></tr></table></figure>


</li>
</ul>
<ul>
<li><p><code>cc_enable_arenas</code>（文件选项）：为C ++生成的代码启用<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Farenas" target="_blank" rel="noopener">竞技场分配</a>。</p>
</li>
<li><p><code>objc_class_prefix</code>（文件选项）：设置Objective-C类前缀，该前缀预先添加到此.proto的所有Objective-C生成的类和枚举中。没有默认值。您应该使用<a href="https://link.juejin.im?target=https%3A%2F%2Fdeveloper.apple.com%2Flibrary%2Fios%2Fdocumentation%2FCocoa%2FConceptual%2FProgrammingWithObjectiveC%2FConventions%2FConventions.html%23%2F%2Fapple_ref%2Fdoc%2Fuid%2FTP40011210-CH10-SW4" target="_blank" rel="noopener">Apple建议的</a> 3-5个大写字符之间的前缀。请注意，Apple保留所有2个字母的前缀。</p>
</li>
<li><p><code>deprecated</code>（字段选项）：如果设置为<code>true</code>，则表示该字段已弃用，新代码不应使用该字段。在大多数语言中，这没有实际效果。在Java中，这成为一个<code>@Deprecated</code>注释。将来，其他特定于语言的代码生成器可能会在字段的访问器上生成弃用注释，这将导致在编译尝试使用该字段的代码时发出警告。如果任何人都没有使用该字段，并且您希望阻止新用户使用该字段，请考虑使用保留语句替换字段声明。</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">int32</span> old_field = <span class="number">6</span> [deprecated = <span class="literal">true</span>];</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h5 id="14-1-自定义选项"><a href="#14-1-自定义选项" class="headerlink" title="14.1 自定义选项"></a>14.1 自定义选项</h5><p>Protocol Buffers还允许您定义和使用自己的选项。这是大多数人不需要的<strong>高级功能</strong>。如果您确实认为需要创建自己的选项，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto.html%23customoptions" target="_blank" rel="noopener">Proto2语言指南</a>以获取详细信息。请注意，创建自定义选项使用的<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fproto.html%23extensions" target="_blank" rel="noopener">扩展名</a>仅允许用于proto3中的自定义选项。</p>
<h3 id="15-生成您的类"><a href="#15-生成您的类" class="headerlink" title="15. 生成您的类"></a>15. 生成您的类</h3><p>根据实际工作需要，生成以下对应语言的自定义消息类型Java，Python，C ++，Go, Ruby, Objective-C，或C＃的<code>.proto</code>文件，你需要运行protobuf 编译器<code>protoc</code>上<code>.proto</code>。如果尚未安装编译器，请<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Fdownloads.html" target="_blank" rel="noopener">下载该软件包</a>并按照自述文件中的说明进行操作。对于Go，您还需要为编译器安装一个特殊的代码生成器插件：您可以在GitHub上的<a href="https://link.juejin.im?target=https%3A%2F%2Fgithub.com%2Fgolang%2Fprotobuf%2F" target="_blank" rel="noopener">golang / protobuf</a>存储库中找到这个和安装说明。</p>
<p>Protobuf 编译器的调用如下：</p>
<figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR  path / to / file .proto</span><br></pre></td></tr></table></figure>

<ul>
<li><p><code>IMPORT_PATH</code>指定<code>.proto</code>解析<code>import</code>指令时在其中查找文件的目录。如果省略，则使用当前目录。可以通过<code>--proto_path</code>多次传递选项来指定多个导入目录; 他们将按顺序搜索。 可以用作简短的形式。 <code>-I=*IMPORT_PATH*``--proto_path</code></p>
</li>
<li><p>您可以提供一个或多个输出指令：</p>
<ul>
<li><code>--cpp_out</code>生成C ++代码<code>DST_DIR</code>。有关更多信息，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fcpp-generated" target="_blank" rel="noopener">C ++生成的代码参考</a>。</li>
<li><code>--java_out</code>生成Java代码<code>DST_DIR</code>。请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fjava-generated" target="_blank" rel="noopener">的Java生成的代码参考</a>更多。</li>
<li><code>--python_out</code>生成Python代码<code>DST_DIR</code>。看到<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fpython-generated" target="_blank" rel="noopener">的Python生成的代码的参考</a>更多。</li>
<li><code>--go_out</code>生成Go代码<code>DST_DIR</code>。有关更多信息，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fgo-generated" target="_blank" rel="noopener">Go生成代码参考</a>。</li>
<li><code>--ruby_out</code>生成Ruby代码<code>DST_DIR</code>。Ruby生成的代码参考即将推出！</li>
<li><code>--objc_out</code>生成Objective-C代码<code>DST_DIR</code>。有关更多信息，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fobjective-c-generated" target="_blank" rel="noopener">Objective-C生成的代码参考</a>。</li>
<li><code>--csharp_out</code>生成C＃代码<code>DST_DIR</code>。有关更多信息，请参阅<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fcsharp-generated" target="_blank" rel="noopener">C＃生成的代码参考</a>。</li>
<li><code>--php_out</code>生成PHP代码<code>DST_DIR</code>。看到<a href="https://link.juejin.im?target=https%3A%2F%2Fdevelopers.google.com%2Fprotocol-buffers%2Fdocs%2Freference%2Fphp-generated" target="_blank" rel="noopener">PHP生成的代码的参考</a>更多。</li>
</ul>
<p>为了方便起见，如果DST_DIR结束于<code>.zip</code>或.<code>jar</code>，编译器会将输出写入具有给定名称的单个ZIP格式存档文件。<code>.jar</code>输出还将根据Java JAR规范的要求提供清单文件。请注意，如果输出存档已存在，则会被覆盖; 编译器不够智能，无法将文件添加到现有存档中。</p>
</li>
<li><p>您必须提供一个或多个<code>.proto</code>文件作为输入。<code>.proto</code>可以一次指定多个文件。虽然文件是相对于当前目录命名的，但每个文件必须位于其中一个文件中，<code>IMPORT_PATH</code>以便编译器可以确定其规范名称。</p>
</li>
</ul>
<h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料:"></a>参考资料:</h3><ul>
<li><a href="https://juejin.im/post/5bb597c2e51d450e6e03e42d" target="_blank" rel="noopener">https://juejin.im/post/5bb597c2e51d450e6e03e42d</a></li>
<li><a href="https://colobu.com/2017/03/16/Protobuf3-language-guide/" target="_blank" rel="noopener">https://colobu.com/2017/03/16/Protobuf3-language-guide/</a></li>
</ul>

    </div>

    
    
    
        
      
        <div id="reward-container">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作, 一个耿直的笑脸~</div>
  <button id="reward-button" disable="enable" onclick="var qr = document.getElementById(&quot;qr&quot;); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
        
      
      <div style="display: inline-block">
        <img src="/images/wechatpay.jpg" alt="Levon 微信支付">
        <p>微信支付</p>
      </div>
        
      
      <div style="display: inline-block">
        <img src="/images/alipay.jpg" alt="Levon 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

      

      <footer class="post-footer">
          
            
          
          <div class="post-tags">
            
              <a href="/tags/golang/" rel="tag"># golang</a>
            
              <a href="/tags/protobuf/" rel="tag"># protobuf</a>
            
          </div>
        

        

          <div class="post-nav">
            <div class="post-nav-next post-nav-item">
              
                <a href="/p/c0435795.html" rel="next" title="grpc在golang_cpp_python下的实践">
                  <i class="fa fa-chevron-left"></i> grpc在golang_cpp_python下的实践
                </a>
              
            </div>

            <span class="post-nav-divider"></span>

            <div class="post-nav-prev post-nav-item">
              
                <a href="/p/937317d6.html" rel="prev" title="为iterm2设置shadowsocks代理">
                  为iterm2设置shadowsocks代理 <i class="fa fa-chevron-right"></i>
                </a>
              
            </div>
          </div>
        
      </footer>
    
  </div>
  
  
  
  </article>

  </div>


          </div>
          
    
    
  <div class="comments" id="comments">
    <div id="disqus_thread">
      <noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
    </div>
  </div>
  
  

        </div>
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">
        
        
        
        
      

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-定义消息类型"><span class="nav-number">1.</span> <span class="nav-text">1. 定义消息类型</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#1-1-指定字段类型"><span class="nav-number">1.0.1.</span> <span class="nav-text">1.1 指定字段类型</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-2-分配标识号"><span class="nav-number">1.0.2.</span> <span class="nav-text">1.2 分配标识号</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-3-指定字段规则"><span class="nav-number">1.0.3.</span> <span class="nav-text">1.3 指定字段规则</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-4-添加更多消息类型"><span class="nav-number">1.0.4.</span> <span class="nav-text">1.4 添加更多消息类型</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-5-添加注释"><span class="nav-number">1.0.5.</span> <span class="nav-text">1.5 添加注释</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-6-保留字段"><span class="nav-number">1.0.6.</span> <span class="nav-text">1.6 保留字段</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#1-7-你的生成是什么-proto？"><span class="nav-number">1.0.7.</span> <span class="nav-text">1.7 你的生成是什么.proto？</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-标量值类型"><span class="nav-number">2.</span> <span class="nav-text">2. 标量值类型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-默认值"><span class="nav-number">3.</span> <span class="nav-text">3. 默认值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-枚举"><span class="nav-number">4.</span> <span class="nav-text">4. 枚举</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#4-1-保留值"><span class="nav-number">4.0.1.</span> <span class="nav-text">4.1 保留值</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-使用其他消息类型"><span class="nav-number">5.</span> <span class="nav-text">5. 使用其他消息类型</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#5-1-导入定义"><span class="nav-number">5.0.1.</span> <span class="nav-text">5.1 导入定义</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#5-2-使用proto2消息类型"><span class="nav-number">5.0.2.</span> <span class="nav-text">5.2 使用proto2消息类型</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-嵌套类型"><span class="nav-number">6.</span> <span class="nav-text">6. 嵌套类型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#7-更新消息类型"><span class="nav-number">7.</span> <span class="nav-text">7. 更新消息类型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#8-未知字段"><span class="nav-number">8.</span> <span class="nav-text">8. 未知字段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#9-任何"><span class="nav-number">9.</span> <span class="nav-text">9. 任何</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#10-Oneof"><span class="nav-number">10.</span> <span class="nav-text">10. Oneof</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#10-1-使用Oneof"><span class="nav-number">10.0.1.</span> <span class="nav-text">10.1 使用Oneof</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#10-2-Oneof-特性"><span class="nav-number">10.0.2.</span> <span class="nav-text">10.2 Oneof 特性</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#10-3-向后兼容性问题"><span class="nav-number">10.0.3.</span> <span class="nav-text">10.3 向后兼容性问题</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#10-4-标签重用问题"><span class="nav-number">10.0.4.</span> <span class="nav-text">10.4 标签重用问题</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#11-地图"><span class="nav-number">11.</span> <span class="nav-text">11. 地图</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#11-1-向后兼容性"><span class="nav-number">11.0.1.</span> <span class="nav-text">11.1 向后兼容性</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#12-包"><span class="nav-number">12.</span> <span class="nav-text">12. 包</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#12-1-包和名称解析"><span class="nav-number">12.0.1.</span> <span class="nav-text">12.1 包和名称解析</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#13-定义服务"><span class="nav-number">13.</span> <span class="nav-text">13. 定义服务</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#14-JSON映射"><span class="nav-number">14.</span> <span class="nav-text">14. JSON映射</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#13-1-JSON选项"><span class="nav-number">14.0.1.</span> <span class="nav-text">13.1 JSON选项</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#14-选项"><span class="nav-number">15.</span> <span class="nav-text">14. 选项</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#14-1-自定义选项"><span class="nav-number">15.0.1.</span> <span class="nav-text">14.1 自定义选项</span></a></li></ol></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#15-生成您的类"><span class="nav-number">16.</span> <span class="nav-text">15. 生成您的类</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#参考资料"><span class="nav-number">17.</span> <span class="nav-text">参考资料:</span></a></li></ol></div>
        
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image"
      src="/images/avatar.jpg"
      alt="Levon">
  <p class="site-author-name" itemprop="name">Levon</p>
  <div class="site-description" itemprop="description">Never Give Up</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        
          <a href="/archives/">
        
          <span class="site-state-item-count">127</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
    
      
      
      <div class="site-state-item site-state-categories">
        
          
            <a href="/categories/">
          
        
        <span class="site-state-item-count">34</span>
        <span class="site-state-item-name">分类</span>
        </a>
      </div>
    
      
      
      <div class="site-state-item site-state-tags">
        
          
            <a href="/tags/">
          
        
        <span class="site-state-item-count">76</span>
        <span class="site-state-item-name">标签</span>
        </a>
      </div>
    
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="https://github.com/unix2dos" title="GitHub &rarr; https://github.com/unix2dos" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
      </span>
    
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="mailto:levonfly@gmail.com" title="E-Mail &rarr; mailto:levonfly@gmail.com" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
      </span>
    
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="https://twitter.com/levonfly" title="Twitter &rarr; https://twitter.com/levonfly" rel="noopener" target="_blank"><i class="fa fa-fw fa-twitter"></i>Twitter</a>
      </span>
    
      <span class="links-of-author-item">
      
      
        
      
      
        
      
        <a href="https://weibo.com/l6241425" title="Weibo &rarr; https://weibo.com/l6241425" rel="noopener" target="_blank"><i class="fa fa-fw fa-weibo"></i>Weibo</a>
      </span>
    
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Levon</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-area-chart"></i>
    </span>
    <span title="站点总字数">507k</span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">7:41</span>
</div>

        












        
      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js?v=3.1.0"></script>
  <script src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  <script src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
<script src="/js/utils.js?v=7.4.0"></script><script src="/js/motion.js?v=7.4.0"></script>
<script src="/js/schemes/pisces.js?v=7.4.0"></script>

<script src="/js/next-boot.js?v=7.4.0"></script>



  
  <script>
    (function(){
      var bp = document.createElement('script');
      var curProtocol = window.location.protocol.split(':')[0];
      bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
    })();
  </script>








  <script src="/js/local-search.js?v=7.4.0"></script>














  

  

  

<script>
  function loadCount() {
    var d = document, s = d.createElement('script');
    s.src = 'https://levonfly.disqus.com/count.js';
    s.id = 'dsq-count-scr';
    (d.head || d.body).appendChild(s);
  }
  // defer loading until the whole page loading is completed
  window.addEventListener('load', loadCount, false);
</script>
<script>
  function disqus_config() {
    this.page.url = "https://unix2dos.github.io/p/21122343.html";
    this.page.identifier = "p/21122343.html";
    this.page.title = 'protobuf3语法指南';};
  function loadComments() {
    if (window.DISQUS) {
      DISQUS.reset({
        reload: true,
        config: disqus_config
      });
    } else {
      var d = document, s = d.createElement('script');
      s.src = 'https://levonfly.disqus.com/embed.js';
      s.setAttribute('data-timestamp', '' + +new Date());
      (d.head || d.body).appendChild(s);
    }
  }
    window.addEventListener('load', loadComments, false);
  
</script>

</body>
</html>
